local U = require "packages/utility/utility"
local EV = {}

--令一名角色明置牌
---@param room Room @ 房间
---@param target ServerPlayer @ 明置牌的角色
---@param cards integer|integer[] @ 明置的牌
---@param skillName? string @ 此次明置的技能名
---@param proposer? ServerPlayer @ 操作者
EV.OpenCards = function (room, target, cards, skillName, proposer)
  skillName = skillName or ""
  cards = type(cards) == "table" and cards or {cards}
  local proposerId = proposer and proposer.id or target.id
  local shown = target:getTableMark("@$shown")
  target:showCards(cards)
  for _, cardId in ipairs(cards) do
    local card = Fk:getCardById(cardId)
    room:setCardMark(card, "@@shown-inhand", 1)
    table.insertIfNeed(shown, cardId)
  end
  room:setPlayerMark(target, "@$shown", shown)
  room.logic:trigger("fk.CardOpened", target, {ids = cards, from = proposerId})
end

--令一名角色暗置牌
---@param room Room @ 房间
---@param target ServerPlayer @ 明置牌的角色
---@param cards integer|integer[] @ 明置的牌
---@param skillName? string @ 此次暗置的技能名
---@param proposer? ServerPlayer @ 操作者
EV.ConcealCards = function (room, target, cards, skillName, proposer)
  skillName = skillName or ""
  cards = type(cards) == "table" and cards or {cards}
  local proposerId = proposer and proposer.id or target.id
  local shown = target:getTableMark("@$shown")
  for _, cardId in ipairs(cards) do
    local card = Fk:getCardById(cardId)
    room:setCardMark(card, "@@shown-inhand", 0)
    table.removeOne(shown, cardId)
  end
  if #shown == 0 then
    room:setPlayerMark(target, "@$shown", 0)
  else
    room:setPlayerMark(target, "@$shown", shown)
  end
  room.logic:trigger("fk.CardConcealed", target, {ids = cards, from = proposerId})
end

local shown_cards_clear = fk.CreateTriggerSkill{
  name = "shown_cards_clear",
  global = true,
  mute = true,
  refresh_events = {fk.GameStart, fk.AfterCardsMove},
  can_refresh = function(self, event, target, player, data)
    if event == fk.GameStart then
      return not player:hasSkill('#shown_cards')
    else
      for _, move in ipairs(data) do
        if move.from == player.id and #player:getTableMark("@$shown") ~= 0 then
          for _, info in ipairs(move.moveInfo) do
            return info.fromArea == Card.PlayerHand
          end
        end
      end
    end
  end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    if event == fk.GameStart then
      room:handleAddLoseSkills(player, '#shown_cards', nil, true, false)
    else
      for _, move in ipairs(data) do
        if move.from == player.id then--改为刷新机制
          local shown = table.filter(player:getCardIds(Player.Hand), function(id) return Fk:getCardById(id):getMark("@@shown-inhand") ~= 0 end)
          if #shown == 0 then
            room:setPlayerMark(player, "@$shown", 0)
          else
            room:setPlayerMark(player, "@$shown", shown)
          end
        end
      end
    end
  end,
}


local shown_cards = fk.CreateVisibilitySkill{
  name = '#shown_cards',
  frequency = Skill.Compulsory,
  card_visible = function(self, player, card)
    local names = {"$ev__huanyan", "#ev__niegao", "$pain", "$yuanpan", "$starfall"}
    if player:hasSkill(self) and table.contains(names, player:getPileNameOfId(card:getEffectiveId())) then
      return false
    end
    if player:hasSkill(self) and card:getMark("@@shown-inhand") ~= 0 and Fk:currentRoom():getCardArea(card) == Card.PlayerHand then
      return true
    end
  end
}

Fk:addSkill(shown_cards)
Fk:addSkill(shown_cards_clear)

Fk:loadTranslationTable{
  ["OpenCards"] = "明置手牌",
  ["ConcealCards"] = "暗置手牌",
  ["@$shown"] = "明置牌",
  ["@@shown-inhand"] = "明置",
  ["judgezone"] = "判定区",

  ["top"] = "牌堆顶",
  ["bottom"] = "牌堆底",
}

--- 类似于askForCardsChosen，适用于“选择每个区域各一张牌”
---@param chooser ServerPlayer @ 要被询问的人
---@param target ServerPlayer @ 被选牌的人
---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h - 手牌区, e - 装备区, j - 判定区， s - 明置手牌
---@param skill_name? string @ 技能名（暂时没用的参数，poxi没提供接口）
---@param prompt? string @ 提示信息（暂时没用的参数，poxi没提供接口）
---@param disable_ids? integer[] @ 不允许选的牌
---@param cancelable? boolean @ 是否可以点取消，默认是
---@return integer[] @ 选择的id
---
EV.askforCardsChosenFromAreas = function(chooser, target, flag, skill_name, prompt, disable_ids, cancelable)
  cancelable = (cancelable == nil) and true or cancelable
  disable_ids = disable_ids or {}
  local visible_data = {}
  local card_data = {}
  if type(flag) ~= "string" then
    flag = "hej"
  end
  local data = {
    to = target.id,
    min = 1,
    max = 3,
    skillName = skill_name,
    prompt = prompt,
  }
  if string.find(flag, "h") and target:getHandcardNum() > 0 then
    local handcards = target:getCardIds(Player.Hand)
    handcards = table.filter(handcards, function (id)
      return not table.contains(disable_ids, id)
    end)
    if #handcards > 0 then
      table.insert(card_data, {"$Hand", handcards})
    end
    for _, id in ipairs(handcards) do
      if not chooser:cardVisible(id) then
        visible_data[tostring(id)] = false
      end
    end
    if next(visible_data) == nil then visible_data = nil end
    data.visible_data = visible_data
  end
  if string.find(flag, "e") and #target:getCardIds("e") > 0 then
    local cards = table.filter(target:getCardIds("e"), function (id)
      return not table.contains(disable_ids, id)
    end)
    if #cards > 0 then
      table.insert(card_data, {"$Equip", cards})
    end
  end
  if string.find(flag, "j") and #target:getCardIds("j") > 0 then
    local cards = table.filter(target:getCardIds("j"), function (id)
      return not table.contains(disable_ids, id)
    end)
    if #cards > 0 then
      table.insert(card_data, {"$Judge", cards})
    end
  end
  if #card_data == 0 then return {} end
  local ret = chooser.room:askForPoxi(chooser, "askforCardsChosenFromAreas", card_data, data, cancelable)
  local result = table.filter(ret, function(id) return id ~= -1 end)
  local hand_num = #ret - #result
  if hand_num > 0 then
    table.insertTable(result, table.random(target:getCardIds("h"), hand_num))
  end
  return result
end

EV.isFriend = function(player, target)
  if player.room and (not player.role_shown or not target.role_shown) then return false end
  if player.role == "" or target.role == "" then return false end
  return target.role == player.role or (table.contains({"lord", "loyalist"}, target.role) and table.contains({"lord", "loyalist"}, player.role))
end

EV.isEnemy = function(player, target)
  if player.room and (not player.role_shown or not target.role_shown) then return false end
  if player.role == "" or target.role == "" then return false end
  return player.role ~= target.role and not (player.role == "lord" and target.role == "loyalist")
  and not (player.role == "loyalist" and target.role == "lord")
end

EV.isSingleTarget = function(card)
  return card.trueName ~= "ghost_assault" and card.trueName ~= "meteor_falling" and card.trueName ~= "god_salvation"
  and card.trueName ~= "banquet" and card.trueName ~= "thorn_circle" and card.trueName ~= "doctrinal_proclama"
  and card.trueName ~= "snow_storm" and card.trueName ~= "armistices"
end

--选择场上的一张牌
EV.chooseCardonBoard = function(room, player, reason, prompt)
  if #table.filter(room.alive_players, function(p) return #p:getCardIds("ej") > 0 end) == 0 then return end
  local _, ret = room:askForUseActiveSkill(player, "choose_players_skill", prompt, true, {
    targets = table.map(table.filter(room.alive_players, function(p)
      return #p:getCardIds("ej") > 0 end), Util.IdMapper),
    num = 1,
    min_num = 0,
    pattern = "",
    skillName = reason
  })
  if ret then
    if #ret.targets == 0 then return end
    room:doIndicate(player.id, ret.targets)
    local id = room:askForCardChosen(player, room:getPlayerById(ret.targets[1]), "ej", reason)
    return id
  end
  return false
end
--调整手牌至n张
EV.adjustHandCards = function(room, player, n, reason)
  if player:getHandcardNum() == n then return end
  if player:getHandcardNum() - n > 0 then
    room:askForDiscard(player, player:getHandcardNum() - n, player:getHandcardNum() - n, false, reason, false)
  elseif n - player:getHandcardNum() > 0 then
    player:drawCards(n - player:getHandcardNum(), reason)
  end
end
--含有的花色数
EV.countSuits = function(cards)
  local suits = {}
  for _, id in ipairs(cards) do
    if Fk:getCardById(id).suit ~= Card.NoSuit then
      table.insertIfNeed(suits, Fk:getCardById(id).suit)
    end
  end
  return #suits
end
--无效牌并播报
EV.nullifiedCard = function(data, card, target)
  if data.card.sub_type == Card.SubtypeDelayedTrick or data.card.type == Card.TypeEquip then
    AimGroup:cancelTarget(data, target.id)
  else
    table.insertIfNeed(data.nullifiedTargets, target.id)
  end
  target.room:sendLog{
    type = "#nullifiedCard",
    from = target.id,
    arg = data.card:toLogString(),
    toast = true,
  }
end

EV.choicelog = function(target, choice)
  target.room:sendLog{
    type = "#ChoiceLogtoast",
    from = target.id,
    arg = choice,
    toast = true,
  }
end

Fk:loadTranslationTable{
  ["#nullifiedCard"] = "%arg 对 %from 无效",
  ["#ChoiceLogtoast"] = "%from 选择了 %arg",
}
--双面将翻面（待修改）
EV.changeHero = function(player, new_general, duputy, clearskills, maxHpChange)
  local room = player.room
  duputy = (duputy == nil) and false or duputy
  clearskills = (clearskills == nil) and false or clearskills
  maxHpChange = (maxHpChange == nil) and true or maxHpChange
  if clearskills then
    local skills = {}
    local other_skills = {}
    for _, s in ipairs(player.player_skills) do
      if s:isPlayerSkill(player) then
        table.insertIfNeed(skills, s.name)
      else
        table.insertIfNeed(other_skills, s.name)
      end
    end
    if #skills > 0 then
      room:handleAddLoseSkills(player, "-"..table.concat(skills, "|-"), nil, true, false)
    end
  end
  local generals = {player.general}
  if player.deputyGeneral ~= "" then
    table.insert(generals, player.deputyGeneral)
  end
  room:returnToGeneralPile(generals)
  room:findGeneral(new_general)
  room:changeHero(player, new_general, false, duputy, true, false)
  if player.dead or not maxHpChange then return false end
  local x = player:getGeneralMaxHp()
  if player.role == "lord" and (#room.players > 4 or room.settings.gameMode == "ev_1v2_mode") then
    x = x + 1
  end
  if player.maxHp ~= x then
    room:changeMaxHp(player, x - player.maxHp)
  end
end

--阵法技

--- 获取与角色成队列的其余角色
---@param player Player
---@return ServerPlayer[]? @ 队列中的角色
EV.getFormationRelation = function(player)
  --if player:isRemoved() then return {} end
  local players = {}
  local p = player
  while true do
    p = p:getNextAlive(false)
    if p == player then break end
    if EV.isFriend(p, player) then
      table.insert(players, p)
    else
      break
    end
  end
  p = player
  while true do
    p = p:getLastAlive(false)
    if p == player then break end
    if EV.isFriend(p, player) then
      table.insertIfNeed(players, p)
    else
      break
    end
  end
  return players
end

--- 确认与某角色是否处于队列中
---@param player Player @ 角色1
---@param target Player @ 角色2，若为 player 即 player 是否处于某一队列
---@return bool
EV.inFormationRelation = function(player, target)
  if target == player then
    return #EV.getFormationRelation(player) > 0
  else
    return table.contains(EV.getFormationRelation(player), target)
  end
end

--- 确认与某角色是否处于围攻关系
---@param player ServerPlayer @ 围攻角色1
---@param target ServerPlayer @ 围攻角色2，可填 player
---@param victim ServerPlayer @ 被围攻角色
---@return bool
EV.inSiegeRelation = function(player, target, victim)
  if not EV.isEnemy(player, victim) or not EV.isFriend(player, target) then return false end
  if player == target then
    return (player:getNextAlive() == victim and player:getNextAlive(false, 2) ~= player and EV.isFriend(player:getNextAlive(false, 2), player))
    or (victim:getNextAlive() == player and player:getLastAlive(false, 2) ~= player and EV.isFriend(player:getLastAlive(false, 2), player))
  else
    return (player:getNextAlive() == victim and victim:getNextAlive() == target) -- P V T
    or (victim:getNextAlive() == player and target:getNextAlive() == victim) -- T V P
  end
end

--获取短路径之间的角色
EV.getShortRoute = function(player, target)
  if player == target then return {} end
  local nexts = {}
  local lasts = {}
  local p = player
  while true do
    p = p:getNextAlive(false)
    table.insert(nexts, p)
    if p == target then break end
  end
  p = player
  while true do
    p = p:getLastAlive(false)
    table.insert(lasts, p)
    if p == target then break end
  end
  if #nexts <= #lasts then
    return nexts
  else
    return lasts
  end
end

--room:setPlayerMark(player, MarkEnum.SwithSkillPreName..self.name, player:getSwitchSkillState(self.name, true))
--player:setSkillUseHistory(self.name, player:usedSkillTimes(self.name, Player.HistoryGame) - 1, Player.HistoryGame)

--中央区

EV.getCenterArea = function (room)
  local ids = room:getBanner("@$centerarea-turn")
  local cards = table.simpleClone(ids)
  for _, id in ipairs(cards) do
    if room:getCardArea(id) ~= Card.DiscardPile then
      table.removeOne(ids, id)
    end
  end
  return ids
end

local CenterArea = fk.CreateTriggerSkill{
  name = "#CenterArea",
  global = true,
  priority = 0.001,
  refresh_events = {fk.AfterCardsMove, fk.AfterDrawPileShuffle, fk.AfterTurnEnd,
    fk.GamePrepared, fk.EventAcquireSkill, fk.EventLoseSkill},
  can_refresh = function(self, event, target, player, data)
    if player.seat == 1 then
      if player.room:getBanner("@$centerarea-turn") ~= nil then
        if event == fk.AfterCardsMove or event == fk.AfterDrawPileShuffle or event == fk.AfterTurnEnd then
          return true
        elseif event == fk.EventLoseSkill then
          return data.CenterArea and table.every(player.room.alive_players, function (p)
            return not table.find(p.player_skills, function (s)
              return s.CenterArea
            end)
          end)
        end
      else
        if event == fk.GamePrepared then
          return table.find(player.room.alive_players, function (p)
            return table.find(p.player_skills, function (s)
              return s.CenterArea
            end)
          end)
        elseif event == fk.EventAcquireSkill then
          return data.CenterArea
        end
      end
    end
  end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    if event == fk.EventLoseSkill then
      room:setBanner("@$centerarea-turn", nil)
    elseif event == fk.AfterTurnEnd then
      room:setBanner("@$centerarea-turn", {})
      room.logic:trigger("fk.CenterareaChanged", player, {ids = {}})
    else
      local cards = table.filter(room.discard_pile, function (id)
        return Fk:getCardById(id):getMark("@@centerarea_fixed") > 0
      end)
      room.logic:getEventsOfScope(GameEvent.MoveCards, 1, function(e)
        for _, move in ipairs(e.data) do
          if move.toArea == Card.DiscardPile then
            for _, info in ipairs(move.moveInfo) do
              table.insertIfNeed(cards, info.cardId)
            end
          end
        end
      end, Player.HistoryTurn)
      cards = table.filter(cards, function (id)
        return table.contains(room.discard_pile, id)
      end)
      room:setBanner("@$centerarea-turn", cards)
      room.logic:trigger("fk.CenterareaChanged", player, {ids = cards})
    end
  end,
}

Fk:addSkill(CenterArea)

--护盾（氯）
local ev_shield_skill = fk.CreateTriggerSkill{
  name = "#ev_shield_skill",
  global = true,
  mute = true,
  events = {fk.DamageInflicted, fk.EventPhaseStart},
  can_trigger = function(self, event, target, player, data)
    if player.room.settings.gameMode == "ls_2v2" or player.room.settings.gameMode == "ls_1v2_mode" then return false end
    if event == fk.EventPhaseStart then
      return target == player and target.phase == Player.Start and #target:getPile("shield") > 0
    else
		  return target == player and #target:getPile("shield") > 0
    end
  end,
  on_cost = Util.TrueFunc,
  on_use = function(self, event, target, player, data)
    local room = player.room
    if event == fk.DamageInflicted then
      if #player:getPile("shield") > data.damage then
        local n = data.damage
        local card = room:askForCard(player, n, n, false, self.name, false, ".|.|.|shield|.|.", "#shield-invoke:::"..n, "shield")
        if #card == 0 then card = table.random(player:getPile("shield"), n) end
        room:moveCardTo(card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = card, from = data.from or nil})
        return true
      else
        data.damage = data.damage - #player:getPile("shield")
        local ids = player:getPile("shield")
        room:moveCardTo(ids, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, "", "", true)
        room.logic:trigger("fk.ShieldCosted", player, {ids = ids, from = data.from or nil})
      end
    else
      room:moveCardTo(player:getPile("shield"), Card.PlayerHand, player, fk.ReasonJustMove, self.name, "", true)
    end
  end
}

Fk:addSkill(ev_shield_skill)

--将cards组成新的牌堆，原牌堆进入place（默认弃牌堆）
EV.CreateDrawpile = function(room, cards, place)
  place = place or Card.DiscardPile
  local drawpile = room.draw_pile
  table.forEach(cards, function(id) return room:setCardArea(id, Card.DrawPile, nil) end)
  table.forEach(drawpile, function(id) return room:setCardArea(id, place, nil) end)
  room.draw_pile = cards
  if place == Card.DiscardPile then
    table.insertTable(room.discard_pile, drawpile)
  end
  table.shuffle(room.draw_pile)
  room:doBroadcastNotify("UpdateDrawPile", tostring(#room.draw_pile))
  room.logic:trigger(fk.AfterDrawPileShuffle, nil, {})
end

Fk:loadTranslationTable{
["#CenterArea"] = "中央区",
["@$centerarea-turn"] = "本回合弃牌堆",

["shield"] = "护盾",
["@@shield"] = "护盾",
["#ev_shield_skill"] = "护盾",
["#shield-invoke"] = "护盾：请弃置 %arg 张护盾抵消 %arg 点伤害",
["#shield_remove"] = "%from 移除了护盾：%arg",
}

return EV